package pub.tbc.api.demo;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

/**
 * @author tbc on 2017/3/21 12:51:38.
 */
@Slf4j
public class ApiDemoServer {

    private class BusinessHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

        @Override
        protected void channelRead0(ChannelHandlerContext context, FullHttpRequest msg) throws Exception {
            log.debug("Http Request : {}", msg);
            new HttpClient(context, msg).connect(new InetSocketAddress("it", 8888));
        }
    }

    private class BusinessClientHandler extends SimpleChannelInboundHandler<FullHttpResponse> {
        private ChannelHandlerContext context;
        private FullHttpRequest request;

        BusinessClientHandler(ChannelHandlerContext context, FullHttpRequest msg) {
            this.context = context;
            this.request = msg;
        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            log.debug("连接第三方服务成功");

        }

        @Override
        protected void channelRead0(ChannelHandlerContext context, FullHttpResponse msg) throws Exception {
            log.debug("read service3 response =>");

            ChannelFuture channelFuture = context.writeAndFlush(msg.retain());
            channelFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) throws Exception {
                    log.debug("全部结束");
                    if (!future.isSuccess()) {
                        future.channel().close().sync();
                    }
                }
            });
        }
    }


    class HttpClient {
        private ChannelHandlerContext context;
        private FullHttpRequest request;

        HttpClient(ChannelHandlerContext context, FullHttpRequest msg) {
            this.context = context;
            this.request = msg;
        }

        void connect(InetSocketAddress address) {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap b = new Bootstrap();
                b.group(group)
                        .channel(NioSocketChannel.class)
                        .option(ChannelOption.TCP_NODELAY, true)
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                // 发消息从下往上走，收消息从上往下的顺序调度执行
                                ch.pipeline()
                                        .addLast("http-decoder", new HttpResponseDecoder())
                                        .addLast("http-aggregator", new HttpObjectAggregator(65536))
                                        .addLast("http-encoder", new HttpRequestEncoder())
                                        .addLast("client-handler", new BusinessClientHandler(context, request));
                            }
                        });
                // 发起异步连接
                ChannelFuture future = b.connect(address);
                // 等待客户端链路关闭
                future.addListener(new GenericFutureListener<Future<? super Void>>() {
                    @Override
                    public void operationComplete(Future<? super Void> future) throws Exception {
                        FullHttpRequest clientRequest = new DefaultFullHttpRequest(
                                HttpVersion.HTTP_1_1,
                                HttpMethod.GET,
                                request.uri()
                        );
//            request.retain();// 当客户端代码中需要保持一个该对象的引用时，调用此方法将计数加1

                        HttpHeaders reqHeaders = request.headers();
                        request.headers().forEach(header -> {
                            if (header.getKey().equalsIgnoreCase("host"))
//                            req.headers().set("Host", "211.157.179.221:22100");
                                request.headers().set("Host", "127.0.0.1:8888");
                            else
                                request.headers().set(header.getKey(), header.getValue());
                        });
                        context.writeAndFlush(clientRequest).addListener(new GenericFutureListener<Future<? super Void>>() {
                            @Override
                            public void operationComplete(Future<? super Void> future) throws Exception {
                                log.debug("请求第三方服务完成");
                            }
                        });
                    }
                });
            } finally {
                group.shutdownGracefully();
            }
        }
    }

    public void bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();


        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HttpRequestDecoder())
                                    .addLast(new HttpObjectAggregator(65535))
                                    .addLast(new HttpResponseEncoder())
                                    .addLast(new BusinessHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 1024);

            ChannelFuture future = b.bind(port).sync();
            System.out.println("started server and listener port : " + port);
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new ApiDemoServer().bind(9999);
    }
}